import 'package:flutter/material.dart';
import 'package:player_plugin/polyv_controller.dart';
import 'package:player_plugin/video_player_value.dart';
import 'package:player_plugin/video_progress_style.dart';

typedef VideoProgressDragHandle = void Function(
  Duration position,
  Duration duration,
);

/// 自定义线性进度条
class VideoLinearProgressBar extends StatefulWidget {
  VideoLinearProgressBar(
    this.controller, {
    Key? key,
    VideoProgressStyle? progressStyle,
    this.allowScrubbing,
    this.padding = const EdgeInsets.only(top: 5.0),
    this.onProgressDrag,
    this.onDragEnd,
    this.onSeekTo,
  })  : progressStyle = progressStyle ?? VideoProgressStyle(),
        super(key: key);

  final PolyvController controller;

  final VideoProgressStyle progressStyle;

  final bool? allowScrubbing;

  final EdgeInsets padding;

  final VideoProgressDragHandle? onProgressDrag;

  final VoidCallback? onDragEnd;

  final ValueChanged<Duration>? onSeekTo;

  @override
  State<StatefulWidget> createState() => _VideoLinearProgressBarState();
}

class _VideoLinearProgressBarState extends State<VideoLinearProgressBar> {
  late VoidCallback listener;

  PolyvController get controller => widget.controller;

  VideoProgressStyle get style => widget.progressStyle;

  Duration? _draggingPosition;

  @override
  void initState() {
    super.initState();
    listener = () {
      if (!mounted) {
        return;
      }
      setState(() {});
    };
    controller.addListener(listener);
  }

  @override
  void deactivate() {
    controller.removeListener(listener);
    super.deactivate();
  }

  @override
  Widget build(BuildContext context) {
    return _VideoScrubber(
      controller: controller,
      handleDrag: widget.onProgressDrag,
      onDragEnd: widget.onDragEnd,
      onSeekTo: widget.onSeekTo,
      onDragUpdate: (Duration? position) {
        _draggingPosition = position;
        setState(() {});
      },
      child: CustomPaint(
        painter: _ProgressBarPainter(
          controller.value,
          style,
          _draggingPosition,
        ),
        child: Container(),
      ),
    );
  }
}

/// 处理进度条手势
class _VideoScrubber extends StatefulWidget {
  const _VideoScrubber({
    required this.child,
    required this.controller,
    this.handleDrag,
    this.onDragEnd,
    this.onSeekTo,
    this.onDragUpdate,
  });

  final Widget child;
  final PolyvController controller;
  final VideoProgressDragHandle? handleDrag;
  final VoidCallback? onDragEnd;
  final ValueChanged<Duration>? onSeekTo;
  final ValueChanged<Duration?>? onDragUpdate;

  @override
  _VideoScrubberState createState() => _VideoScrubberState();
}

class _VideoScrubberState extends State<_VideoScrubber> {
  PolyvController get controller => widget.controller;

  Widget get child => widget.child;

  VideoProgressDragHandle? get handleDrag => widget.handleDrag;

  VoidCallback? get onDragEnd => widget.onDragEnd;

  ValueChanged<Duration>? get onSeekTo => widget.onSeekTo;

  ValueChanged<Duration?>? get onDragUpdate => widget.onDragUpdate;
  Duration _draggingPosition = Duration.zero;

  @override
  Widget build(BuildContext context) {
    void emitDragUpdate(globalPosition) {
      if (handleDrag != null) {
        final RenderBox box = context.findRenderObject() as RenderBox;
        final Offset tapPos = box.globalToLocal(globalPosition);
        final double relative = tapPos.dx / box.size.width;
        Duration position = controller.value.duration * relative;
        if (position.inSeconds < 0) {
          position = const Duration(seconds: 0);
        }
        if (position.inSeconds > controller.value.duration.inSeconds) {
          position = controller.value.duration;
        }
        handleDrag?.call(position, controller.value.duration);
      }
    }

    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      child: child,
      onHorizontalDragStart: (DragStartDetails details) {
        if (!controller.value.isInitialized) {
          return;
        }
        _draggingPosition = controller.value.position;
      },
      onHorizontalDragUpdate: (DragUpdateDetails details) {
        if (!controller.value.isInitialized) {
          return;
        }
        emitDragUpdate(details.globalPosition);
        _draggingPosition = seekToRelativePosition(details.globalPosition);
        onDragUpdate?.call(_draggingPosition);
      },
      onHorizontalDragEnd: (DragEndDetails details) {
        onDragEnd?.call();
        onSeekTo?.call(_draggingPosition);
        onDragUpdate?.call(null);
      },
      onTapDown: (TapDownDetails details) {
        if (!controller.value.isInitialized) {
          return;
        }
        _draggingPosition = seekToRelativePosition(details.globalPosition);
        onSeekTo?.call(_draggingPosition);
      },
    );
  }

  Duration seekToRelativePosition(Offset globalPosition) {
    final RenderBox box = context.findRenderObject() as RenderBox;
    final Offset tapPos = box.globalToLocal(globalPosition);
    final double relative = tapPos.dx / box.size.width;
    final Duration position = controller.value.duration * relative;
    return position;
  }
}

/// 绘制进度条
class _ProgressBarPainter extends CustomPainter {
  _ProgressBarPainter(
    this.value,
    this.style,
    this.draggingPosition,
  );

  final VideoPlayerValue value;
  final VideoProgressStyle style;
  final Duration? draggingPosition;

  //刷新布局的时候告诉flutter 是否需要重绘
  @override
  bool shouldRepaint(CustomPainter painter) {
    return true;
  }

  //绘制流程
  @override
  void paint(Canvas canvas, Size size) {
    final baseOffset = size.height / 2 - style.height / 2.0;

    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromPoints(
          Offset(0.0, baseOffset),
          Offset(size.width, baseOffset + style.height),
        ),
        Radius.circular(style.progressRadius),
      ),
      Paint()..color = style.backgroundColor,
    );
    if (!value.isInitialized) {
      return;
    }
    late double playedPartPercent;
    if (draggingPosition != null) {
      playedPartPercent =
          draggingPosition!.inMilliseconds / value.duration.inMilliseconds;
    } else {
      playedPartPercent =
          value.position.inMilliseconds / value.duration.inMilliseconds;
    }
    final double playedPart =
        playedPartPercent > 1 ? size.width : playedPartPercent * size.width;
    const double start = 0.0;
    final double end = value.bufferPercentage / 100 * size.width;
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromPoints(
          Offset(start, baseOffset),
          Offset(end, baseOffset + style.height),
        ),
        Radius.circular(style.progressRadius),
      ),
      Paint()..color = style.bufferedColor,
    );
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromPoints(
          Offset(0.0, baseOffset),
          Offset(playedPart, baseOffset + style.height),
        ),
        Radius.circular(style.progressRadius),
      ),
      Paint()..color = style.playedColor,
    );

    final shadowPath = Path()
      ..addOval(
        Rect.fromCircle(
          center: Offset(playedPart, baseOffset + style.height / 2),
          radius: style.dragHeight,
        ),
      );

    canvas.drawShadow(shadowPath, Colors.black, 0.2, false);
    canvas.drawCircle(
      Offset(playedPart, baseOffset + style.height / 2),
      style.dragHeight,
      Paint()..color = style.dragBarColor,
    );
  }
}
